#pragma once

#define M_PI       3.14159265358979323846   // pi



template<class T>
struct d_vec2
{
	T x{ 0 }, y{0};
	d_vec2(T _x, T _y, T _z) :x(_x), y(_y){}
	__DEVICE__ d_vec2<T>& normalize()
	{
		float len = x * x + y * y;
		if (len != 0) len = sqrtf(len);
		x /= len, y /= len;
		return *this;
	}

	__DEVICE__ float length() const { return sqrtf(x * x + y * y); }
	// dot product
	//__DEVICE__ float operator * (const d_vec2<T>& v) const { return x * v.x + y * v.y; }
	__DEVICE__ d_vec2<T> operator - (const d_vec2<T>& v) const { return d_vec2<T>{ x - v.x, y - v.y}; }
	__DEVICE__ d_vec2<T> operator - () const { return d_vec2<T>{ -x, -y}; }
	__DEVICE__ d_vec2<T> operator + (const d_vec2<T>& v) const { return d_vec2<T>{ x + v.x, y + v.y}; }
	__DEVICE__ d_vec2<T>& operator += (const d_vec2<T>& v) { x += v.x, y += v.y;		return *this; }
	__DEVICE__ d_vec2<T>& operator *= (const float& r) { x *= r, y *= r;		return *this; }
	__DEVICE__ friend d_vec2<T> operator * (const float& r, const d_vec2<T>& v) { return d_vec2<T>{ v.x* r, v.y* r}; }
	__DEVICE__ d_vec2<T> operator * (const float& r) const { return d_vec2<T>{ x* r, y* r}; }

};

template<class T>
struct d_vec3
{
	using type = d_vec3<T>;
	T x{ 0 }, y{ 0 }, z{0};
	__DEVICE__ d_vec3(){}
	__DEVICE__ d_vec3(T _x, T _y, T _z) :x(_x), y(_y), z(_z) {}
	__DEVICE__ d_vec3(T v) :x(v), y(v), z(v) {}
	//__DEVICE__ d_vec3<T>& normalize()
	//{
	//	float len = x * x + y * y + z * z;
	//	if (len != 0) len = sqrtf(len);
	//	x /= len, y /= len, z /= len;
	//	return *this;
	//}

	__DEVICE__ float length() const { return sqrtf(x * x + y * y + z * z); }
	// dot product
	__DEVICE__ d_vec3<T> operator * (const d_vec3<T>& v) const { return d_vec3<T>(x * v.x, y * v.y, z * v.z); }
	__DEVICE__ d_vec3<T> operator - (const d_vec3<T>& v) const { return d_vec3<T>{ x - v.x, y - v.y, z - v.z }; }
	__DEVICE__ d_vec3<T> operator - () const { return d_vec3<T>{ -x, -y, -z }; }
	__DEVICE__ d_vec3<T> operator + (const d_vec3<T>& v) const { return d_vec3<T>{ x + v.x, y + v.y, z + v.z }; }
	__DEVICE__ d_vec3<T>& operator += (const d_vec3<T>& v) { x += v.x, y += v.y, z += v.z; return *this; }
	__DEVICE__ d_vec3<T>& operator *= (const float& r) { x *= r, y *= r, z *= r; return *this; }
	__DEVICE__ d_vec3<T>& operator *= (const d_vec3<T>& v) { x *= v.x,y *= v.y,z *= v.z; return *this; }
	__DEVICE__ friend d_vec3<T> operator * (const float& r, const d_vec3<T>& v) { return d_vec3<T>{ v.x* r, v.y* r, v.z* r }; }
	__DEVICE__ d_vec3<T> operator * (const float& r) const { return d_vec3<T>{ x* r, y* r, z* r }; }
};

template<class T>
__DEVICE__ d_vec3<T> normalize(const d_vec3<T>& v)
{
	float len = v.x * v.x + v.y * v.y + v.z * v.z;
	if (len != 0) len = sqrtf(len);
	return d_vec3<T>(v.x / len, v.y / len, v.z / len);
}
template<class T>
__DEVICE__ double dot(const d_vec3<T>& l, const d_vec3<T>& r)
{
	return l.x * r.x + l.y * r.y + l.z * r.z;
}

template<class T>
__DEVICE__ double dot(const d_vec2<T>& l, const d_vec2<T>& r)
{
	return l.x * r.x + l.y * r.y;
}
template<class T>
struct d_vec4
{
	T x{ 0 }, y{ 0 }, z{ 0 }, w{ 0 };

	__DEVICE__ d_vec4<T>& normalize()
	{
		float len = x * x + y * y + z * z;
		if (len != 0) len = sqrtf(len);
		x /= len, y /= len, z /= len;
		return *this;
	}

	__DEVICE__ float length() const { return sqrtf(x * x + y * y + z * z); }
	// dot product
	//__DEVICE__ float operator * (const d_vec4<T>& v) const { return x * v.x + y * v.y + z * v.z; }
	__DEVICE__ d_vec4<T> operator - (const d_vec4<T>& v) const { return d_vec4<T>{ x - v.x, y - v.y, z - v.z }; }
	__DEVICE__ d_vec4<T> operator - () const { return d_vec4<T>{ -x, -y, -z }; }
	__DEVICE__ d_vec4<T> operator + (const d_vec4<T>& v) const { return d_vec4<T>{ x + v.x, y + v.y, z + v.z }; }
	__DEVICE__ d_vec4<T>& operator += (const d_vec4<T>& v) { x += v.x, y += v.y, z += v.z; return *this; }
	__DEVICE__ d_vec4<T>& operator *= (const float& r) { x *= r, y *= r, z *= r; return *this; }
	__DEVICE__ friend d_vec4<T> operator * (const float& r, const d_vec4<T>& v) { return d_vec4<T>{ v.x* r, v.y* r, v.z* r }; }
	__DEVICE__ d_vec4<T> operator * (const float& r) const { return d_vec4<T>{ x* r, y* r, z* r }; }
};


using vec2 = d_vec2<float>;
using vec3 = d_vec3<float>;
using vec4 = d_vec3<float>;

__DEVICE__ float d_fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
__DEVICE__ float d_lerp(float t, float a, float b) { return a + t * (b - a); }
__DEVICE__ float d_clamp(float lo, float hi, float x) { return x < lo ? lo : (x > hi ? hi : x); }
__DEVICE__ float d_grad(int hash, float x, float y, float z)
{
	int h = hash & 15;
	float u = h < 8 ? x : y,
		v = h < 4 ? y : h == 12 || h == 14 ? x : z;
	return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
__DEVICE__ float smoothstep(float lo, float hi, float x)
{
	float t = d_clamp((x - lo) / (hi - lo), 0.f, 1.f);
	return t * t * (3.0 - (2.0 * t));
}
template<class T> __DEVICE__ T d_max(const T& l, const T& r) { return l > r ? l : r; }
template<class T> __DEVICE__ T d_min(const T& l, const T& r) { return l < r ? l : r; }

__DEVICE__ vec3 exp(const vec3 v)
{
	return vec3(exp(v.x), exp(v.y), exp(v.z));
}